/**
 * original author: https://github.com/xfgryujk
 * 
 * source-code: https://github.com/xfgryujk/weibo-img-crypto
 */

import { RandomSequence } from "./random.js"

const seed = "114514"

/**
 * @param {ImageData} ImgData 
 */
export function encryptImg(ImgData) {
  const ed = Codec.createCodec(ImgData)
  return ed.encrypt()
}
/**
 * @param {ImageData} ImgData 
 */
export function decryptImg(ImgData) {
  const ed = Codec.createCodec(ImgData)
  return ed.decrypt()
}

function createImageData(width, height) {
  return new Uint8Array(width * height * 4).fill(255)
}

class Codec {
  /**
   * @param {ImageData} imgData 
   */
  constructor(imgData) {
    this._imgData = imgData
  }
  // 加密，返回加密后的imgData
  encrypt() { }
  // 解密，返回解密后的imgData
  decrypt() { }
}
Codec._codecClasses = {}

/**
 * 
 * @param {string} name 
 * @param {ImageData} imgData 
 * @returns 
 */
Codec.createCodec = function (imgData) {
  let CodecClass = Codec._codecClasses.ShuffleBlockCodec
  return new CodecClass(imgData)
}

class ShuffleBlockCodec extends Codec {
  constructor(imgData) {
    super(imgData)
  }
  encrypt() {
    return this._doCommon((result, blockX, blockY, newBlockX, newBlockY) =>
      this._copyBlock(result, newBlockX, newBlockY, this._imgData, blockX, blockY)
    )
  }

  decrypt() {
    return this._doCommon((result, blockX, blockY, newBlockX, newBlockY) =>
      this._copyBlock(result, blockX, blockY, this._imgData, newBlockX, newBlockY)
    )
  }

  _doCommon(handleCopy) {
    // 尺寸不是8的倍数则去掉边界
    let blockWidth = Math.floor(this._imgData.width / 8)
    let blockHeight = Math.floor(this._imgData.height / 8)
    let result = {
      width: blockWidth * 8, height: blockHeight * 8,
      data: createImageData(blockWidth * 8, blockHeight * 8)
    }
    let seq = new RandomSequence(blockWidth * blockHeight, seed)

    for (let blockY = 0; blockY < blockHeight; blockY++) {
      for (let blockX = 0; blockX < blockWidth; blockX++) {
        let index = seq.next()
        let newBlockX = index % blockWidth
        let newBlockY = Math.floor(index / blockWidth)
        handleCopy(result, blockX, blockY, newBlockX, newBlockY)
      }
    }
    return result
  }

  _copyBlock(dstImgData, dstBlockX, dstBlockY, srcImgData, srcBlockX, srcBlockY) {
    let iDstStart = (dstBlockY * dstImgData.width + dstBlockX) * 8 * 4
    let iSrcStart = (srcBlockY * srcImgData.width + srcBlockX) * 8 * 4
    for (let y = 0; y < 8; y++) {
      for (let i = 0; i < 8 * 4; i++) {
        dstImgData.data[iDstStart + i] = srcImgData.data[iSrcStart + i]
      }
      iDstStart += dstImgData.width * 4
      iSrcStart += srcImgData.width * 4
    }
  }
}

Codec._codecClasses.ShuffleBlockCodec = ShuffleBlockCodec
